#include "cpu/decode/modrm.h"
#include "cpu/helper.h"

//从指令指针处加载地址
int load_addr(swaddr_t eip, ModR_M *m, Operand *rm) {
	assert(m->mod != 3);

	int32_t disp = 0;								//偏移量
	int instr_len, disp_offset, disp_size = 4;		//指令长度、偏移量和偏移大小
	int base_reg = -1, index_reg = -1, scale = 0;	//基址寄存器、索引寄存器和缩放因子
	swaddr_t addr = 0;								//地址初始化

	//检查R_M字段是否为ESP
	if(m->R_M == R_ESP) {
		SIB s;		
		s.val = instr_fetch(eip + 1, 1);	//从指令流中获取SIB字节
		base_reg = s.base;					//设置基址寄存器
		disp_offset = 2;					//偏移量位置
		scale = s.ss;						//获取缩放因子
		//若索引寄存器不为ESP，设置索引寄存器
		if(s.index != R_ESP) { index_reg = s.index; }
	}
	else {
		/* no SIB *///若无SIB直接使用R_M字段
		base_reg = m->R_M;
		disp_offset = 1;					//偏移量位置
	}

	//根据mod字段值设置偏移量大小
	if(m->mod == 0) {
		if(base_reg == R_EBP) { base_reg = -1; }		//若基址寄存器为EBP,则不使用基址
		else { disp_size = 0; }							//否则没有偏移量
	}
	//mod为1时，偏移量大小为1字节
	else if(m->mod == 1) { disp_size = 1; }

	instr_len = disp_offset;		//初始化指令长度
	if(disp_size != 0) {
		/* has disp */
		disp = instr_fetch(eip + disp_offset, disp_size);	//从指令流中获取偏移量
		if(disp_size == 1) { disp = (int8_t)disp; }			//若偏移量为1字节则转换为8位有符号数

		instr_len += disp_size;		//更新指令长度
		addr += disp;				//将偏移量添加到地址
	}

	//将基址寄存器的值添加到地址
	if(base_reg != -1) {
		addr += reg_l(base_reg);
	}

	//将索引寄存器的值添加到地址并乘缩放因子
	if(index_reg != -1) {
		addr += reg_l(index_reg) << scale;
	}

#ifdef DEBUG
	char disp_buf[16];		//偏移量缓冲区
	char base_buf[8];		//基址缓冲区
	char index_buf[8];		//索引缓冲区

	if(disp_size != 0) {
		/* has disp */
		sprintf(disp_buf, "%s%#x", (disp < 0 ? "-" : ""), (disp < 0 ? -disp : disp));
	}
	else { disp_buf[0] = '\0'; }

	if(base_reg == -1) { base_buf[0] = '\0'; }
	else { 
		sprintf(base_buf, "%%%s", regsl[base_reg]); 
	}

	if(index_reg == -1) { index_buf[0] = '\0'; }
	else { 
		sprintf(index_buf, ",%%%s,%d", regsl[index_reg], 1 << scale); 
	}

	//根据基址和索引的存在格式化rm字符串
	if(base_reg == -1 && index_reg == -1) {
		sprintf(rm->str, "%s", disp_buf);
	}
	else {
		sprintf(rm->str, "%s(%s%s)", disp_buf, base_buf, index_buf);
	}
#endif

	rm->type = OP_TYPE_MEM;		//设置操作数类型为内存
	rm->addr = addr;			//设置操作数地址

	return instr_len;			//返回指令长度
}

//读取ModR/M字节
int read_ModR_M(swaddr_t eip, Operand *rm, Operand *reg) {
	ModR_M m;
	m.val = instr_fetch(eip, 1);	//从字节流中读取ModR/M字节
	reg->type = OP_TYPE_REG;
	reg->reg = m.reg;

	//若mod为3则直接使用寄存器
	if(m.mod == 3) {
		rm->type = OP_TYPE_REG;
		rm->reg = m.R_M;
		//根据操作数大小读取寄存器值
		switch(rm->size) {
			case 1: rm->val = reg_b(m.R_M); break;
			case 2: rm->val = reg_w(m.R_M); break;
			case 4: rm->val = reg_l(m.R_M); break;
			default: assert(0);
		}
#ifdef DEBUG
		//格式化寄存器字符串
		switch(rm->size) {
			case 1: sprintf(rm->str, "%%%s", regsb[m.R_M]); break;
			case 2: sprintf(rm->str, "%%%s", regsw[m.R_M]); break;
			case 4: sprintf(rm->str, "%%%s", regsl[m.R_M]); break;
		}
#endif
		return 1;
	}
	else {
		//若不为寄存器，加载地址
		int instr_len = load_addr(eip, &m, rm);
		//根据rm寄存器类型设置当前段寄存器
		if (rm -> reg == R_EBP || rm -> reg == R_ESP) {
      		current_sreg = R_SS;
    	} else {
      	current_sreg = R_DS;
    }
		rm->val = swaddr_read(rm->addr, rm->size);
		return instr_len;
	}
}

